import { onCleanup } from "solid-js";
// source code see: https://github.com/wobsoriano/solid-zustand/blob/main/src/createWithStore.ts
import { createStore, reconcile } from "solid-js/store";
import type { StateCreator, StoreApi } from "zustand/vanilla";
import { createStore as createZustandStore } from "zustand/vanilla";
import type { Create, ExtractState } from "./types";

export function useStore<S extends StoreApi<unknown>>(api: S): ExtractState<S>;

export function useStore<S extends StoreApi<unknown>, U>(
  api: S,
  selector: (state: ExtractState<S>) => U,
  equalityFn?: (a: U, b: U) => boolean,
): U;

export function useStore<TState extends object, StateSlice>(
  api: StoreApi<TState>,

  // biome-ignore lint/suspicious/noExplicitAny: xxx
  selector: (state: TState) => StateSlice = api.getState as any,
  equalityFn?: (a: StateSlice, b: StateSlice) => boolean,
) {
  // biome-ignore lint/suspicious/noExplicitAny: xxx
  const initialValue = selector(api.getState()) as any;
  const [state, setState] = createStore(initialValue);

  const listener = (nextState: TState, previousState: TState) => {
    const prevStateSlice = selector(previousState);
    const nextStateSlice = selector(nextState);

    if (equalityFn !== undefined) {
      if (!equalityFn(prevStateSlice, nextStateSlice)) setState(reconcile(nextStateSlice));
    } else {
      setState(reconcile(nextStateSlice));
    }
  };

  const unsubscribe = api.subscribe(listener);
  onCleanup(() => unsubscribe());
  return state;
}

function createImpl<T extends object>(createState: StateCreator<T, [], []>) {
  const api = typeof createState === "function" ? createZustandStore(createState) : createState;

  // biome-ignore lint/suspicious/noExplicitAny: xxx
  const useBoundStore: any = (selector?: any, equalityFn?: any) => useStore(api, selector, equalityFn);

  Object.assign(useBoundStore, api);

  return useBoundStore;
}

const create = (<T extends object>(createState: StateCreator<T, [], []> | undefined) =>
  createState ? createImpl(createState) : createImpl) as Create<"store">;

export default create;
